"""--------------------------------------------------------------------------------------------------
Filename:   controls.py
Author:     Chris Chester (christopher.chester@pg.canterbury.ac.nz) adapted from Ben Han (uha10@uclive.ac.nz)
Date:       4th September 2012
Description:
    Control panel for the plotting mode. 
    Provides controls for trials during live data acquisition and plotting.
--------------------------------------------------------------------------------------------------"""

import wx, sys

ID_AUTO_CONNECT = 5
DEFAULT_SEPARATION = 5
#===============================================================================
# CLASS: ControlPanel(wx.Frame)
#    This class is derived from wx.Panel and contains the controls for the plot
#         - BoundControlBox for Y axis options
#         - CheckboxPanel for display options
#         - ButtonPanel for control options (start/stop, menu)
#         - SerialPanel for serial port settings
#===============================================================================
class ControlPanel(wx.Frame):
    """ Main control panel for training modes
    """
    def __init__(self, app, parent=None, ID=-1, title="Control Panel"):
        wx.Frame.__init__(self, parent, ID, title, pos=(0,0), size=(600, 400))
        panel = wx.Panel(self, -1)
        self.app = panel.app = app
        
        ## initial value of _y_axis_max read and set for plot
        self.ymax_ctrl = BoundControlBox(panel, -1, "Y max", app.cfg.Y_AXIS_MAX_INIT)
        self.chkbx_ctrl = CheckboxPanel(panel, -1, "Display")
        self.but_ctrl = ButtonPanel(panel, -1)
        self.serial_ctrl = SerialPanel(panel, -1)

        self.Bind(wx.EVT_RADIOBUTTON, self.on_radiobutton)

        # Create a box and sizer
        box = wx.StaticBox(panel, -1, "Control Panel")
        sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)        

        # Add controls to the sizer
        BWIDTH = 3     
        
        sizer.Add(self.ymax_ctrl, border=BWIDTH, flag=wx.ALL | wx.EXPAND)
        sizer.Add(self.chkbx_ctrl, border=BWIDTH, flag=wx.ALL | wx.EXPAND)
        sizer.Add(self.serial_ctrl, border=BWIDTH, flag=wx.ALL | wx.EXPAND)
        sizer.Add(self.but_ctrl, border=BWIDTH, flag=wx.ALL | wx.EXPAND)
        
        
        panel.SetSizer(sizer)
        sizer.Fit(self)
                
    def ctrl_update(self):
        ''' Update the controls from the config file '''
        self.serial_ctrl.update_port(self.app.cfg)
                
    def on_radiobutton(self, event):
        ''' Sets the ymax value based on which radio button is pressed '''
        if event.GetId() == 1:
            self.app.sess_status.ymax_auto = True
        else:
            self.app.sess_status.ymax_auto = False


#===============================================================================
#  CLASS: BoundControlBox
#     This class is used for the y axis control on the main control panel.
#===============================================================================
class BoundControlBox(wx.Panel):
    """ A static box with a couple of radio buttons and a text
        box. Allows to switch between an automatic mode and a 
        manual mode to control the y-axis range maximum. The y-axis 
        minimum is always zero.
    """
    def __init__(self, parent, ID, label, initval):
        wx.Panel.__init__(self, parent, ID)
        self.app = parent.app
        self.value = initval

        box = wx.StaticBox(self, -1, label)
        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)

        self.radio_manual = wx.RadioButton(self, -1, label="Manual", style=wx.RB_GROUP)
        self.manual_text = wx.TextCtrl(self, -1, size=(35, -1), value=str(initval), style=wx.TE_PROCESS_ENTER)
        self.radio_auto = wx.RadioButton(self, 1, label="Auto")
        self.radio_auto.SetValue(True)
        self.app.sess_status.ymax_auto = True

        self.Bind(wx.EVT_UPDATE_UI, self.on_update_manual_text, self.manual_text)
        self.Bind(wx.EVT_TEXT_ENTER, self.on_text_enter, self.manual_text)

        manual_box = wx.BoxSizer(wx.HORIZONTAL)
        manual_box.Add(self.radio_manual, flag=wx.ALIGN_CENTER_VERTICAL)
        manual_box.Add(self.manual_text, flag=wx.ALIGN_CENTER_VERTICAL)

        sizer.Add(manual_box, 0, wx.ALL, DEFAULT_SEPARATION)
        sizer.Add(self.radio_auto, 0, wx.ALL, DEFAULT_SEPARATION)

        self.SetSizer(sizer)
        sizer.Fit(self)

    def on_update_manual_text(self, event):
        ''' Decides whether to turn on the manual axis text box input'''
        self.manual_text.Enable(self.radio_manual.GetValue())

    def on_text_enter(self, event):
        ''' Response to text input in the manual axis text box '''
        value = self.manual_text.GetValue()
        self.app.cfg._y_axis_max = int(value)
        axes = self.app.plot.get_axes()
        axes.set_ybound(lower=0, upper=self.app.cfg._y_axis_max)

    def is_auto(self):
        ''' Determines whether the auto radio button is pressed
            output - button status '''
        return self.radio_auto.GetValue()

    def get_value(self):
        ''' Returns value of the yaxis max
            output - Y axis max value'''
        return self.value

    def set_value(self, newval):
        ''' Set the maximum y axis value
            input - new maximum '''
        self.value = newval

    def set_auto(self, boo):
        ''' Turn auto y max on/off
            input - boolean value corresponding to auto button status '''
        self.radio_auto.SetValue(boo)


#===============================================================================
#  CLASS: CheckboxPanel
#     This class is used to select display options in the main control panel.
#===============================================================================
class CheckboxPanel(wx.Panel):
    def __init__(self, parent, ID, label):
        wx.Panel.__init__(self, parent, ID)
        self.app = parent.app

        box = wx.StaticBox(self, -1, label)
        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)

        self.cb_grid = wx.CheckBox(self, -1, "Grid")

        self.Bind(wx.EVT_CHECKBOX, self.on_cb_grid, self.cb_grid)


        self.__set_properties()
        
        sizer.Add(self.cb_grid, 0, wx.ALL, DEFAULT_SEPARATION)

        self.SetSizer(sizer)
        sizer.Fit(self)

    def __set_properties(self):
        ''' Set check box properties'''
        cfg = self.app.cfg
        self.cb_grid.SetValue(cfg.default_grid)
        
    def on_cb_grid(self, event):
        ''' Turn on grid check box'''
        self.app.plot.plotctrl.cb_grid.SetValue(self.cb_grid.GetValue())
        self.app.plot.draw_grid(self.cb_grid.GetValue())



#===============================================================================
#  CLASS: ButtonPanel
#     This class contains the control buttons which start and stop the trials
#===============================================================================
class ButtonPanel(wx.Panel):
    def __init__(self, parent, ID):
        wx.Panel.__init__(self, parent, ID)
        self.app = parent.app
        
        # Create buttons
        self.start_sess_button = wx.Button(self, -1, "Start")
        self.menu_button = wx.Button(self, -1, "Exit to menu")

        self.__set_properties()
        self.__do_layout()        
        # Sets handlers
        self.Bind(wx.EVT_BUTTON, self.app.phdls.start_sess_button, self.start_sess_button)
        self.Bind(wx.EVT_BUTTON, self.app.phdls.exit_plot, self.menu_button)
        
    def __set_properties(self):
        ''' Set session button properties'''
        XSIZE = 150
        YSIZE = 35
        
        self.start_sess_button.Disable()
        self.start_sess_button.SetMinSize((XSIZE, YSIZE))
        self.menu_button.SetMinSize((XSIZE, YSIZE))
        
    def __do_layout(self):
        ''' Layout of session buttons in control GUI '''
        # Add buttons to sizer
        sizer_new_session_staticbox = wx.StaticBox(self, -1, "Start new session")
        sizer_new_session = wx.StaticBoxSizer(sizer_new_session_staticbox, wx.VERTICAL)
        sizer_controls = wx.BoxSizer(wx.VERTICAL)
        
        sizer_new_session.Add(self.start_sess_button, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, DEFAULT_SEPARATION)
        sizer_controls.Add(sizer_new_session, 1, wx.ALL|wx.EXPAND, DEFAULT_SEPARATION)
        sizer_controls.Add(self.menu_button, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, DEFAULT_SEPARATION)

        self.SetSizer(sizer_controls)
        sizer_controls.Fit(self)
        
    def enable_start(self, boolean=True):
        ''' Allow for session to be initiated'''
        self.start_sess_button.Enable(boolean)


#===============================================================================
# CLASS: SerialPanel
#    This class contains a control widget to set the port value of the 
#    serial console.
#===============================================================================    
class SerialPanel(wx.Panel):
    def __init__(self, parent, ID):
        wx.Panel.__init__(self, parent, ID)
        self.app = parent.app
        
        self.sizer_outer_box_staticbox = wx.StaticBox(self, -1, "Serial port control")
        self.label_port = wx.StaticText(self, -1, "Port number:")
        self.s_pin_port = wx.SpinCtrl(self, -1, min=1, max=100, initial=int(self.app.cfg.port[3:]), name='Port number')
        self.button_connect = wx.Button(self, -1, "Connect")
        self.button_disconnect = wx.Button(self, -1, "Disconnect")
        self.button_auto_connect = wx.Button(self, -1, "Auto connect")
        self.connected_text = wx.StaticText(self, -1,"False")
        self.connected_label = wx.StaticText(self, -1, "Connected:")
        
        self.button_auto_connect.SetMinSize((150, 35))
        
        self.__set_properties()
        self.__do_layout()

        self.Bind(wx.EVT_SPINCTRL, self.app.phdls.serial_port_change, self.s_pin_port)
        self.Bind(wx.EVT_BUTTON, self.app.phdls.serial_connect, self.button_connect)
        self.Bind(wx.EVT_BUTTON, self.app.phdls.serial_disconnect, self.button_disconnect)
        self.Bind(wx.EVT_BUTTON, self.app.phdls.auto_connect, self.button_auto_connect)

    def __set_properties(self):
        ''' Set properties for connection GUI '''
        
        self.s_pin_port.SetFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, "MS Shell Dlg 2"))
        self.button_disconnect.Disable()

        self.connected_text.SetForegroundColour(wx.Colour(255, 0, 0))

    def __do_layout(self):
        ''' Layout of connection components for connection GUI '''
        
        sizer_outer_box = wx.StaticBoxSizer(self.sizer_outer_box_staticbox, wx.VERTICAL)
        sizer_buttons = wx.GridSizer(1, 2, 0, 0)
        sizer_port_select = wx.BoxSizer(wx.HORIZONTAL)
        
        self.sizer_auto_static = wx.StaticBox(self, -1, "")
        self.sizer_connected_static = wx.StaticBox(self, -1, "")
        self.sizer_auto_button_static = wx.StaticBox(self, -1, "")
        self.sizer_manual_static = wx.StaticBox(self, -1, "Manual")
        
        sizer_auto_button = wx.StaticBoxSizer(self.sizer_auto_button_static, wx.VERTICAL)
        sizer_connected = wx.StaticBoxSizer(self.sizer_connected_static, wx.VERTICAL)
        sizer_auto = wx.StaticBoxSizer(self.sizer_auto_static, wx.HORIZONTAL)
        sizer_manual = wx.StaticBoxSizer(self.sizer_manual_static, wx.HORIZONTAL)
        
        sizer_auto_button.Add(self.button_auto_connect, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, DEFAULT_SEPARATION)
        sizer_connected.Add(self.connected_label, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, DEFAULT_SEPARATION)
        sizer_connected.Add(self.connected_text, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, DEFAULT_SEPARATION)
        sizer_auto.Add(sizer_auto_button, 0, wx.EXPAND, DEFAULT_SEPARATION)
        sizer_auto.Add(sizer_connected, 1, wx.EXPAND, DEFAULT_SEPARATION)
        
        sizer_manual.Add(self.label_port, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL)
        sizer_manual.Add(self.s_pin_port, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL)
        sizer_manual.Add(sizer_port_select, 0, wx.EXPAND)
        sizer_buttons.Add(self.button_connect, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL,DEFAULT_SEPARATION)
        sizer_buttons.Add(self.button_disconnect, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL,DEFAULT_SEPARATION)
        sizer_manual.Add(sizer_buttons, 0, wx.EXPAND)
        
        sizer_outer_box.Add(sizer_auto, 1, wx.EXPAND, DEFAULT_SEPARATION)
        sizer_outer_box.Add(sizer_manual, 1, wx.EXPAND, DEFAULT_SEPARATION)
        self.SetSizer(sizer_outer_box)
        sizer_outer_box.Fit(self)
        
    def update_port(self, cfg):
        ''' Set the port number value from the string contained in cfg 
            inputs - configuration object, '''
        s_int = int(cfg._port[3:])
        self.s_pin_port.SetValue(s_int)
        
# End of controls.py